package com.ly.fn.biz.ins.srv.api.util;

import com.ly.fn.biz.ins.common.term.annotation.Func;
import lombok.SneakyThrows;
import org.apache.commons.compress.utils.Lists;
import org.objectweb.asm.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.File;
import java.io.FileOutputStream;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import static org.objectweb.asm.Opcodes.*;


public class ASMUtil {

    public ASMUtil() {

    }

    public static Class<?>  generateTermService2Controller(String packageName, Class<?> termServiceClz) {

        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
        String controllerName = termServiceClz.getSimpleName() + "Controller";
        String classPath = packageName.replace('.', '/') + "/" + controllerName;
        // Define the class as public and extending Object
        cw.visit(V1_8, ACC_PUBLIC + ACC_SUPER, classPath, null, "java/lang/Object", null);

        // 添加构造器
        MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "()V", null, null);
        mv.visitVarInsn(Opcodes.ALOAD, 0);
        mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
        mv.visitInsn(Opcodes.RETURN);
        mv.visitMaxs(1, 1);
        mv.visitEnd();

        String funcServiceName = toLowerCaseFirst(termServiceClz.getSimpleName()) + "Service";

        // 添加自定义类注解和属性注入
        // Add @RestController annotation to the class
        AnnotationVisitor av = cw.visitAnnotation(Type.getDescriptor(RestController.class), true);
        av.visitEnd();

        // Add @RequestMapping annotation to the class
        av = cw.visitAnnotation(Type.getDescriptor(RequestMapping.class), true);
        AnnotationVisitor arrayVisitor = av.visitArray("value");
        arrayVisitor.visit(null, "/");
        arrayVisitor.visitEnd();
        av.visitEnd();


        // Add an @Autowired field
        FieldVisitor fv = cw.visitField(ACC_PRIVATE, "service", Type.getDescriptor(termServiceClz), null, null);
        av = fv.visitAnnotation(Type.getDescriptor(Autowired.class), true);

        //解决一个接口多个实现问题
        String qualifierName = "";
        if (!StringUtils.isEmpty(qualifierName)) {
            av = fv.visitAnnotation(Type.getDescriptor(Qualifier.class), true);
            arrayVisitor = av.visitArray("value");
            arrayVisitor.visit(null, qualifierName);
            arrayVisitor.visitEnd();
        }
        av.visitEnd();
        fv.visitEnd();

        //生成方法
        generateControllerMethods(cw, termServiceClz,funcServiceName);

        cw.visitEnd();

        exportToDirectory(cw.toByteArray(), controllerName, "/Users/mac3/clz");
        DynamicClassLoader classLoader = new DynamicClassLoader();
        controllerName = classPath.replace("/", ".");
        Class<?> generatedClass = classLoader.defineClassFromBytecode(controllerName, cw.toByteArray());

        return generatedClass;
    }

    public static void generateControllerMethods(ClassWriter cw,Class<?> termServiceClz,String autowiredSrvName) {
        String postMapping = PostMapping.class.getName().replace(".", "/");
        String className = termServiceClz.getName().replace(".", "/");
        Method[] methods = termServiceClz.getMethods();
        Map<String, List<Method>> methodMap = new HashMap<>();
        for (Method method : methods) {
            Func func = method.getAnnotation(Func.class);
            if (Objects.nonNull(func)) {
                List<Method> methodList = methodMap.get(method.getName());
                if (methodList == null) {
                    methodList = Lists.newArrayList();
                }
                methodList.add(method);
                methodMap.put(method.getName(), methodList);
            }
        }
        for (Map.Entry<String, List<Method>> entry : methodMap.entrySet()) {
            List<Method> methodList = entry.getValue();
            boolean hasMulit = methodList.size() > 1 ? true : false;
            int index = 1;
            for (Method serviceMethod : methodList) {

                String methodName = serviceMethod.getName();

                Class<?>[] paramTypes = serviceMethod.getParameterTypes();
                Class<?> returnType = serviceMethod.getReturnType();
                String methodDescriptor = Type.getMethodDescriptor(serviceMethod);

                String genMethodName = methodName + (hasMulit ? "_" + index : "");
                MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, genMethodName, methodDescriptor, null, null);

                // 添加HTTP Mapping注解
                AnnotationVisitor avMapping = mv.visitAnnotation(postMapping.replace('.', '/'), true);
                avMapping.visit("value", genMethodName);
                avMapping.visitEnd();

                // 添加SneakyThrows 注解
                AnnotationVisitor sneakyThrows = mv.visitAnnotation(SneakyThrows.class.getName().replace('.', '/'), true);
                sneakyThrows.visitEnd();

                // 方法开始
                mv.visitCode();
                // 加载this引用到操作数栈
                mv.visitVarInsn(ALOAD, 0);

                mv.visitFieldInsn(GETFIELD, className, autowiredSrvName,Type.getDescriptor(termServiceClz));

                // 加载所有参数
                for (int i = 0; i < paramTypes.length; i++) {

                    AnnotationVisitor requestBody = mv.visitParameterAnnotation(i,Type.getDescriptor(RequestBody.class), true);
                    requestBody.visitEnd();

                    mv.visitVarInsn(ALOAD, i + 1);
                }

                // 调用Service方法
                mv.visitMethodInsn(INVOKEVIRTUAL, className, serviceMethod.getName(), methodDescriptor, false);

                // 返回值
                if (returnType.equals(Void.TYPE)) {
                    mv.visitInsn(RETURN);
                } else {
                    mv.visitInsn(Type.getType(returnType).getOpcode(Opcodes.IRETURN));
                }

                mv.visitMaxs(0, 0);
                mv.visitEnd();

                index++;
            }
        }
    }


    private static void exportToDirectory(byte[] bytecode, String fileName, String directory) {

        try {
            File file = new File(directory);
            if (!file.exists()) {
                file.mkdirs();
            }
            // 获取类的字节数组


            // 将字节数组写入本地文件

            FileOutputStream fos = new FileOutputStream(directory + "/" + fileName +".class");

            fos.write(bytecode);

            fos.close();
        } catch (Exception ex) {

        }
    }

    private static String toLowerCaseFirst(String word) {
        if (word == null || word.isEmpty()) {
            return word;
        }
        char firstChar = word.charAt(0);
        char lowerFirstChar = Character.toLowerCase(firstChar);
        return lowerFirstChar + word.substring(1);
    }
}